import oracledb from "oracledb"
import type { Pool, BindParameters } from "oracledb"
import fs from "fs"
import readline from "readline"
import { logger } from "../utils"
import type {
  Ipool,
  Iconnection,
  IdbConfig,
  IpoolFlag,
  IdataRow,
} from "./types"
import type { Ioracle, IreturnObj, Ioptions } from "../types"

const readFile: Function = function (path: string): Promise<string[]> {
  const rl = readline.createInterface({
    input: fs.createReadStream(path).setEncoding("utf8"),
  })
  return new Promise((resolve, _reject) => {
    const array: string[] = []
    rl.on("line", (line) => {
      array.push(line)
    })
    rl.on("close", () => {
      resolve(array)
    })
  })
}

//判断连接池是否已打开
const poolFlag: IpoolFlag = {}
//连接池
const pool: Ipool = {}
const connection: Iconnection = {}

class CONNECTDB implements Ioracle {
  /**
   * execute执行sql语句并返回结果
   *
   * @param {Object} option 設定項
   * @param {string} option.sql 執行語句
   * @param {string} option.path 連接數據庫
   * @param {string} option.params 可選項 執行參數 多個執行語句時才有用
   * @param {boolean} option.isMulti 可選項 是否為多個執行語句
   * @param {string} option.type 可選項 執行方式 默認為query
   * @return {Object} 返回查詢結果或執行結果
   */

  private execute(options: {
    sql: string
    path: string
    params?: BindParameters[]
    isMulti?: boolean
  }): Promise<IreturnObj> {
    const { sql, path, isMulti = false, params = [{}] } = options
    return new Promise(async (resolve, reject) => {
      //讀取數據庫參數
      const dbConfig: string[] = await readFile(path)
      let temp: string[] = []
      let poolName: string
      temp = dbConfig[0].split(",")
      const DB_CONFIG: IdbConfig = {
        _enableStats: true,
        user: temp[0],
        password: temp[1],
        connectString: temp[2],
        poolAlias: temp[3],
      }
      poolName = temp[3]

      try {
        if (!poolFlag[poolName]) {
          poolFlag[poolName] = true
          pool[poolName] = await this.init(DB_CONFIG)
          logger.info("连接池已打开")
        }

        connection[poolName] =
          connection[poolName] || (await pool[poolName].getConnection())

        //執行多個sql
        //params:eg:[{ a: 1, b: "One" },{ a: 2, b: "Two" },{ a: 3, b: "Three" }]
        if (isMulti && params.length) {
          //记录调用的sql
          logger.info(sql, params)
          await (await connection[poolName])
            .executeMany(sql, params)
            .catch(async (err) => {
              if (await connection[poolName]) {
                await (await connection[poolName]).rollback()
                logger.error((err as Error).message)
                reject({ code: 0, message: (err as Error).message, data: {} })
              }
            })
          resolve({ code: 200, message: "success", data: [] })
        } else {
          //查询
          logger.info(sql)
          const { metaData = [], rows = [] } = await (
            await connection[poolName]
          ).execute<string>(sql)

          if (metaData.length > 0) {
            const data: IdataRow[] = []
            for (let i = 0; i < rows.length; i++) {
              const dataRow: IdataRow = {}
              for (let j = 0; j < metaData.length; j++) {
                dataRow[metaData[j]["name"].toLowerCase()] =
                  rows[i][j] !== null ? rows[i][j] : ""
                //数字保留两位小数
                // rows[i][j] !== null
                //   ? isNaN(Number(rows[i][j]))
                //     ? rows[i][j]
                //     : Math.round(Number(rows[i][j])*100)/100
                //   : ""
              }
              data.push(dataRow)
            }
            resolve({ code: 200, message: "success", data })
          } else {
            //非查询
            resolve({ code: 200, message: "success", data: [] })
          }
        }
      } catch (err) {
        logger.error(`execute-error:${(err as Error).message}`)
        reject({ code: 0, message: (err as Error).message, data: [] })
      } finally {
        if (await connection[poolName]) {
          try {
            await (await connection[poolName]).commit()
          } catch (err) {
            logger.error(`commit-error:${err}`)
          }
        }
      }
    })
  }

  private init(DB_CONFIG: IdbConfig): Promise<Pool> {
    return oracledb.createPool(DB_CONFIG)
  }

  connHrm(options: Ioptions) {
    return this.execute({ ...options, path: "./lib/connect_cchrm.ini" })
  }
  connChaos(options: Ioptions) {
    return this.execute({ ...options, path: "./lib/connect_chaos.ini" })
  }
  connHrTest(options: Ioptions) {
    return this.execute({ ...options, path: "./lib/connect_cchrtest.ini" })
  }
  connChaosTest(options: Ioptions) {
    return this.execute({ ...options, path: "./lib/connect_chaostest.ini" })
  }
  connCcapps(options: Ioptions) {
    return this.execute({ ...options, path: "./lib/connect_ccapps.ini" })
  }
  connAppsTest(options: Ioptions) {
    return this.execute({ ...options, path: "./lib/connect_appstest.ini" })
  }
  connFpgFlow(options: Ioptions) {
    return this.execute({ ...options, path: "./lib/connect_fpgflow.ini" })
  }
  connCtShopFloor(options: Ioptions) {
    return this.execute({ ...options, path: "./lib/connect_ctshopfloor.ini" })
  }
}

export default new CONNECTDB()
